﻿using System;
using System.Windows.Forms;
using System.Drawing;
using Qurihara;
using System.Collections;
using System.Xml;
using Qurihara.Anm;
//using System.IO;
using OSC.NET;
using System.Threading;
using System.Net;
using Qurihara.QBasic;
using System.Text;
//using System.Collections.Generic;
//using Microsoft.Ink;
//using KotodamaPlugin;


namespace Qurihara.OSCCom
{
    public class OSCComCore :IComCore
    {
        //OSC.NET
        private OSCReceiver receiver = null;
        private OSCTransmitter transmitter = null;
        private Thread listenerThread = null;

        //protected System.Windows.Forms.Timer timer;
        //private static Mutex mutInvalidated = new Mutex();

        private static bool received = false;
        private static string _adr, _data;

        protected string name = "default_name";
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        protected string ip = "localhost";
        public string Ip
        {
            get { return ip; }
        }
        protected int mPort = 5501;
        public int MPort
        {
            get { return mPort; }
        }
        private static int mPortMax = 5700;
        public event MessageEventHandler MessageReceived;

        private bool connected = false;
        public bool IsConnected
        {
            get { return connected; }
        }

        public OSCComCore()
        {
        }

        #region Configuration

        public virtual void Initialize()
        {
            //timer = new System.Windows.Forms.Timer();
            //timer.Interval = 200;
            //timer.Tick += new EventHandler(timer_Tick);
            //timer.Enabled = true;
            //timer.Start();
            InitOSC();
        }
        public virtual void Dispose()
        {
            Disconnect();
            //timer.Stop();
            //timer.Enabled = false;
            //timer.Dispose();
        }

        private void InitOSC()
        {
            string p = "";
            //if (Util.GetStringProperty("OscQRViewClientPlugin.MyPort", ref p))
            if (Util.GetStringProperty("OSCCom.MyPort", ref p))
            {
                mPort = int.Parse(p);
            }

            // mPortはどんどんインクリメントしてチェックしましょう。
            connected = false;
            while (!connected)
            {
                try
                {
                    connected = Connect(mPort);
                }
                catch
                {
                    if (mPort < mPortMax)
                    {
                        //MessageBox.Show(mPort.ToString()+"番ポートが開けませんでした。続行します。");
                    }
                    else
                    {
                        MessageBox.Show(mPort.ToString() + "番ポートが開けませんでした。あきらめます。");
                        break;
                    }
                    mPort++;
                }
            }
            Util.GetStringProperty("Base.InstanceId", ref name);
            //AskId(name);
        }

        private bool Connect(int port)
        {
            receiver = new OSCReceiver(mPort);
            listenerThread = new Thread(new ThreadStart(this.Receive));
            listenerThread.Start();
            Application.ApplicationExit += new EventHandler(Application_ApplicationExit);

            string strHost = Dns.GetHostName();
            ip = this.GetIPAddress(strHost);

            return true;
        }

        #region get IP
        /// <summary>
        /// 指定したホスト名またはURLのIPアドレスを取得する
        /// </summary>
        /// <param name="HostName">IPアドレスを取得するホスト名またはURL</param>
        /// <returns>IPアドレス(文字列)</returns>
        private string GetIPAddress(string HostName)
        {

            // 変数を宣言
            string strIP;
            IPHostEntry IPHstEnt;
            IPAddress IPAddrs;

            try
            {
                // コンピュータ名を指定してIPアドレスリストを取得
                IPHstEnt = System.Net.Dns.GetHostByName(HostName);

                //
                int adapterIndex = 0;
                string p = "";
                //if (Util.GetStringProperty("OscQRViewClientPlugin.AdapterIndex", ref p))
                if (Util.GetStringProperty("OSCCom.AdapterIndex", ref p))
                {
                    adapterIndex = int.Parse(p);
                }

                // IPリストの0番目を取得
                // 0番目はコンピュータのIPアドレス
                //IPAddrs = IPHstEnt.AddressList[0];//[1];//[0];
                IPAddrs = IPHstEnt.AddressList[adapterIndex];
                strIP = IPAddrs.ToString();
            }
            catch (System.Net.Sockets.SocketException SockEx)
            {
                // HostNameを解決出来ない場合の例外処理を記述
                strIP = "";
            }
            catch (System.Security.SecurityException SecuEx)
            {
                // アクセスするためのアクセス許可がなかった場合の例外処理を記述
                strIP = "";
            }

            return strIP;

        }
        #endregion

        private void Application_ApplicationExit(object sender, EventArgs e)
        {
            Disconnect();
        }

        private void Disconnect()
        {
            if (transmitter != null) transmitter.Close();
            if (receiver != null) receiver.Close();
            if (listenerThread != null) listenerThread.Abort();
            listenerThread = null;
            receiver = null;
            transmitter = null;
        }

        public void ConnectTransmitter(string serverIP, int serverPort)
        {
            transmitter = new OSCTransmitter(serverIP, serverPort);
        }

        #endregion

        public void Send(string senderName,string destName, string adr, string val)
        {
            Send(adr, val);
        }
        public void Send(string adr, string val)
        {
            OSCMessage msg = new OSCMessage(adr);
            try
            {
                msg.Append(int.Parse(val));
            }
            catch
            {
                msg.Append(val);
            }
            transmitter.Send(msg);
        }

        private void Receive()
        {
            while (true)
            {
                try
                {
                    OSCPacket msg = receiver.Receive();
                    string adr = msg.Address;
                    ArrayList objs = msg.Values;
                    string data = "";
                    bool first = true;
                    foreach (object obj in objs)
                    {
                        if (first) first = false;
                        else data += ", ";
                        data += obj.ToString();
                    }
                    _Parse(adr, data);
                }
                catch
                {
                }
            }
        }

        //protected static string prefix = "";//"/kotodama/qurihara";
        //public string Prefix
        //{
        //    get { return prefix; }
        //}
        private void _Parse(string adr, string data)
        {
            //mutInvalidated.WaitOne();
            _adr = adr;
            _data = data;
            if (MessageReceived != null) MessageReceived(this, new MessageEventArgs(DateTime.Now, "@any", "@any", _adr, _data));
            //received = true;
            //mutInvalidated.ReleaseMutex();
        }

        //private void timer_Tick(object sender, EventArgs e)
        //{
        //    mutInvalidated.WaitOne();
        //    if (received)
        //    {
        //        received = false;
        //        try
        //        {
        //            //Parse(_adr, _data);
        //            if(MessageReceived != null) MessageReceived(this,new MessageEventArgs("@any","@any",_adr,_data));
        //        }
        //        catch { }
        //    }
        //    mutInvalidated.ReleaseMutex();
        //}

    }
}
